package com.deepface.common.utils.ssh2Utils;

import com.jcraft.jsch.*;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Vector;
import java.util.concurrent.TimeUnit;
import org.springframework.web.multipart.MultipartFile;

/**
 * @author zbr
 * @version 1.0
 * @description
 * @date 2023/6/5
 */
public class SSHTools {

  private Session session;
  /**
   * 获取实例
   *
   * @return 实例
   */
  public static SSHTools getInstance() {
    return new SSHTools();
  }

  /**
   * 登录主机
   * <p>
   * 密码登录
   *
   * @param hostIp   主机IP
   * @param hostPort 主机ssh端口
   * @param user     主机登录用户名
   * @param password 主机登录密码
   */
  public void loginByPassword(String hostIp,
                              Integer hostPort,
                              String user,
                              String password) {
    try {
      JSch jsch = new JSch();
      session = jsch.getSession(user, hostIp, hostPort);
      session.setPassword(password);
      // 设置第一次登陆的时候提示，可选值:(ask | yes | no)
      session.setConfig("StrictHostKeyChecking", "no");
      // 连接超时
      session.connect(1000 * 10);
    } catch (JSchException e) {
      System.out.println(e);
      System.out.println("登录时发生错误!"+ e);
    }
  }

  /**
   * 登录后，主机执行指定命令
   *
   * @param command 命令
   * @return 执行命令后结果
   */
  public String executeShell(String command) throws Exception {
    System.out.println("exe cmd: {}"+ command);

    byte[] tmp = new byte[1024];
    // 命令返回的结果
    StringBuilder resultBuffer = new StringBuilder();

    ChannelExec exec = (ChannelExec) session.openChannel("exec");
    exec.setCommand(command);
    exec.connect();

    // 返回结果流（命令执行错误的信息通过getErrStream获取）
    InputStream stdStream1 = exec.getInputStream();
    try {
      // 开始获得SSH命令的结果
      while (true) {
        while (stdStream1.available() > 0) {
          int i = stdStream1.read(tmp, 0, 1024);
          if (i < 0) {
            break;
          }
          resultBuffer.append(new String(tmp, 0, i));
        }
        if (exec.isClosed()) {
          break;
        }
        try {
          Thread.sleep(200);
        } catch (Exception e) {
          System.out.println("执行命令发生错误!"+ e);
        }
      }
    }
    finally {
      if (exec.isConnected()) {
        exec.disconnect();
      }
    }
    String result = resultBuffer.toString();
    System.out.println("exe cmd return : {}"+ result);
    return result;
  }

  private int findMaxExistingIndex(ChannelSftp channel) throws SftpException {
    Vector<ChannelSftp.LsEntry> fileList = channel.ls(".");
    int maxIndex = -1;

    for (ChannelSftp.LsEntry entry : fileList) {
      String filename = entry.getFilename();
      if (filename.matches("11_\\d{5}\\.jpg")) {
        String indexStr = filename.substring(3, 8);
        int index = Integer.parseInt(indexStr);
        maxIndex = Math.max(maxIndex, index);
      }
    }

    return maxIndex;
  }

  private String getNextFileName(int index) {
    return "11_" + String.format("%05d", index) + ".jpg";
  }

  public void uploadLocalFile(MultipartFile[] multipartFiles, String remotePath) {
      ChannelSftp channel = null;
      try {
        channel = (ChannelSftp) session.openChannel("sftp");
        channel.connect(1000 * 10);

        if (!(remotePath == null || remotePath.trim().length() == 0)) {
          channel.cd(remotePath);//进入指定目录
        }

        int maxExistingIndex = findMaxExistingIndex(channel);
        int nextIndex = maxExistingIndex + 1;

        for (MultipartFile multipartFile : multipartFiles) {
          //String fileName = multipartFile.getOriginalFilename();
          String fileName = getNextFileName(nextIndex);
          nextIndex++;

          InputStream is = multipartFile.getInputStream();
          channel.put(is, fileName);
          is.close();
        }
      } catch (Exception e0) {
        throw new RuntimeException(e0);
      } finally {
        if (channel != null) {
          channel.disconnect();
        }
      }
  }

  /**
   * 上传文件
   * @param localPath
   * @param remotePath
   * @throws JSchException
   */
  public void uploadLocalFile_raw(String localPath, String remotePath) {
    File file = new File(localPath);
    System.out.println("localPath:"+localPath);

    if (file.exists()) {
      System.out.println("源文件存在，正常执行");
      ChannelSftp channel = null;
      try {
        channel = (ChannelSftp) session.openChannel("sftp");
        channel.connect(1000 * 10);

        if (!(remotePath == null || remotePath.trim().length() == 0)) {
          channel.cd(remotePath);//进入指定目录
        }
        InputStream is = new FileInputStream(file);
        channel.put(is, file.getName());
        is.close();
      } catch (Exception e0) {
        throw new RuntimeException(e0);
      } finally {
        if (channel != null) {
          channel.disconnect();
        }
//        if (session != null) {
//          session.disconnect();
//        }
      }
    } else {
      System.out.println("源文件不存在");
    }
  }

  /**
   * 关闭连接
   */
  public void close() {
    if (Objects.nonNull(session) && session.isConnected()) {
      session.disconnect();
    }
  }

  public String ssh2Shell(String[] commands) {
    ChannelShell channelShell = null;

    byte[] buffer = new byte[1024];
    StringBuilder resultBuffer = new StringBuilder();
    try {
      channelShell = (ChannelShell) session.openChannel("shell");
      channelShell.setPty(true);
      channelShell.connect();
      try (InputStream in = channelShell.getInputStream();
           OutputStream out = channelShell.getOutputStream();
           PrintWriter pw = new PrintWriter(out);) {
        for (String cmd : commands) {
          pw.println(cmd);
          System.out.println("执行的命令是："+cmd);
        }
        pw.println("exit");
        System.out.println("exit");
        pw.flush();

        while (true) {
          while (in.available() > 0) {
            int i = in.read(buffer, 0, 1024);
            if (i < 0) {
              break;
            }
            resultBuffer.append(new String(buffer, 0, i));
          }
          if (channelShell.isClosed()) {
            System.out.println("SSH2 Exit: " + channelShell.getExitStatus());
            break;
          }
          try {
            TimeUnit.SECONDS.sleep(2);
          } catch (Exception e) {
          }
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if (channelShell != null) {
        channelShell.disconnect();
      }
      if (session != null) {
        session.disconnect();
      }
    }
    String result = resultBuffer.toString();
    System.out.println("exe cmd return : {}"+ result+"完成");
    return result;
  }


  /**
   * 执行相关的命令（交互式）
   * @param command
   * @return
   */
  public List<String> execMultiCmd(String command) throws Exception {
    ChannelShell channel = null;
    PrintWriter printWriter = null;
    BufferedReader input = null;
    List<String> stdout  = new ArrayList<>();

    if(session == null){
      throw new Exception("请先执行connect");
    }

    try {
      //建立交互式通道
      channel = (ChannelShell) session.openChannel("shell");
      channel.connect();
      //获取输入
      InputStreamReader inputStreamReader = new InputStreamReader(channel.getInputStream());
      input = new BufferedReader(inputStreamReader);
      //输出
      printWriter = new PrintWriter(channel.getOutputStream());
      printWriter.println(command);
      printWriter.println("exit");
      printWriter.flush();
      System.out.println("The remote command is: ");
      String line;
      while ((line = input.readLine()) != null) {
        stdout.add(line);
        System.out.println("add:"+line);
      }
    } catch (Exception e) {
      System.out.println(e.getMessage());
      return null;
    }finally {
      printWriter.close();
      input.close();
      if (channel != null) {
        //关闭通道
        channel.disconnect();
      }
    }
    return stdout;
  }

  /**
   * 得到目标目录remotepath下所有前缀为prefix的文件名称
   * @param remotePath
   * @param prefix
   */
  public List<String> getFileNameList( String remotePath, String prefix){
    List<String> res=new ArrayList<>();
    try{
      ChannelSftp channel = null;
      channel = (ChannelSftp) session.openChannel("sftp");
      channel.connect(1000 * 10);

      if (!(remotePath == null || remotePath.trim().length() == 0)) {
        System.out.println("remotePath是"+remotePath);
        channel.cd(remotePath);//进入指定目录
      }
      List<LsEntry> files=channel.ls(prefix+"*");
      for (LsEntry entry : files)
      {
        if (!entry.getFilename().equals(".") && !entry.getFilename().equals(".."))
        {
          res.add(entry.getFilename());
        }
      }
      System.out.println("print"+res);
    }catch (Exception e){
      e.printStackTrace();
    }
    return res;
  }

  /**
   * 得到目标路径remotepath文件的inputstream流
   * @param remotePath
   */
  public InputStream getFileStream( String remotePath) {
    InputStream res=null;
    try {
      ChannelSftp channel = null;
      channel = (ChannelSftp) session.openChannel("sftp");
      channel.connect();
      res=channel.get(remotePath);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return res;
  }

  /**
   * 文件下载
   * @param localPath
   * @param remotePath
   */
  public String downloadFile(String localPath, String remotePath) {
    Channel channel = null;
    ChannelSftp channelSftp = null;

    Boolean haveResult=false;
    String returnStr="";

    try {
      channel = session.openChannel("sftp");
      channel.connect(1000 * 10);
      channelSftp = (ChannelSftp) channel;
      channelSftp.cd(remotePath);
      Vector<LsEntry> files = channelSftp.ls(remotePath);
      String[] fileNames = new String[files.size()];
      //获取文件夹中所有文件名字
      for (int i = 0; i < files.size(); i++) {
        fileNames[i] = files.get(i).getFilename();
        }
      int count = 0;
      //遍历remoteDirPath 下的每一个文件
      for (String fileName : fileNames) {
        count+=1;
        if(!fileName.equals(".") && !fileName.equals("..") ){
          haveResult=true;
          System.out.println(fileName);
          //如果把remoteDirPath 当作一级目录，那remoteFilePath 就是第二级目录
          System.out.println("remoteFilePath:"+remotePath);
          System.out.println("localPath:"+localPath);
          System.out.println("-------------"+session.getPort());
          channelSftp.get(remotePath+"/test_texture.ply",localPath);
          System.out.println("get完成");
        }
        }
      if(!haveResult) returnStr="文件还在生成";
      else  returnStr="文件下载完成";
      } catch (Exception e0) {
      throw new RuntimeException(e0);
      } finally {
      if (channel != null) {
        channel.disconnect();
        }
      return returnStr;
      }
    }

}
